package com.winhxd.b2c.product.service.impl;

import com.winhxd.b2c.common.constant.BusinessCode;
import com.winhxd.b2c.common.domain.product.condition.ProductStockSkuCondition;
import com.winhxd.b2c.common.domain.product.condition.ProductStockSpuCondition;
import com.winhxd.b2c.common.domain.product.model.ProductStock;
import com.winhxd.b2c.common.domain.product.vo.PriceProgramIdVO;
import com.winhxd.b2c.common.domain.product.vo.ProductSpuIdVO;
import com.winhxd.b2c.common.domain.search.dto.SkuChanged;
import com.winhxd.b2c.common.exception.BusinessException;
import com.winhxd.b2c.common.feign.search.ProductSearchClient;
import com.winhxd.b2c.common.util.JsonUtil;
import com.winhxd.b2c.product.dao.ProductPriceSkuMapper;
import com.winhxd.b2c.product.dao.ProductSkuMapper;
import com.winhxd.b2c.product.service.ProductStockService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author cuiyueqiang
 * @Description 创建订单减库存增销量,取消订单增库存减销量
 * @Date 2019/1/12 17:00
 **/

@Service
public class ProductStockServiceImpl implements ProductStockService {

    Logger logger = LoggerFactory.getLogger(ProductStockServiceImpl.class);

    @Autowired
    private ProductSkuMapper productSkuDao;

    @Autowired
    private ProductPriceSkuMapper productPriceSkuDao;

    @Autowired
    private ProductSearchClient productSearchClient;

    @Autowired
    private RedissonClient redissonClient;

    @Value("${redis.cluster.lockOutLet}")
    private String lockOutLet;

    @Value("${redis.cluster.lockWaitTime}")
    private Integer lockWaitTime;

    @Value("${redis.cluster.lockLeaseTime}")
    private Integer lockLeaseTime;

    @Transactional(rollbackFor = {BusinessException.class, Exception.class},isolation = Isolation.READ_COMMITTED)
    @Override
    public Integer operateProductStock(List<ProductStockSkuCondition> condition) {
        //condition 入参sku集合检验
        if (CollectionUtils.isEmpty(condition)) {
            throw new BusinessException(BusinessCode.CODE_1026);
        }
        //根据skuId排序
        List<ProductStockSkuCondition> conditionSort = getSortBySkuId(condition);
        logger.info("增减库存接口入参==================" + JsonUtil.toJSONString(conditionSort));
        int updateStockCount = 0;
        List<RLock> rLockList = null;
        try{
            //skuId批量加锁
            rLockList = this.tryLock(conditionSort);
            //处理业务逻辑，增减库存,销量及同步es
            updateStockCount = handleStock(conditionSort);
        } finally {
            //释放锁
            if(rLockList != null){
                this.unlock(rLockList);
            }
        }
        return  updateStockCount;
    }
    
    /** 
     *
     * @Author cuiyueqiang
     * @Description skuId批量加锁
     * @Date 2019/1/18 19:14
     * @param conditionSort
     * @return 
     **/
    private List<RLock> tryLock(List<ProductStockSkuCondition> conditionSort){
        List<RLock> rLockList = new ArrayList<RLock>();
        try{
            //根据skuId批量加锁
            for (ProductStockSkuCondition skuStock : conditionSort) {
                Long skuId = skuStock.getSkuId();
                RLock skuLock = redissonClient.getLock(lockOutLet + ":" + skuId);
                //lockWaitTime最长等待时间，lockLeaseTime自动解锁时间
                boolean yes = skuLock.tryLock(lockWaitTime,lockLeaseTime, TimeUnit.SECONDS);
                if(yes){
                    rLockList.add(skuLock);
                    logger.info("skuId:" + skuId +"====================================已获取锁");
                } else{
                    logger.info("skuId:" + skuId +"====================================获取锁超时");
                    throw new BusinessException(BusinessCode.CODE_811001);
                }
            }
        } catch (BusinessException | InterruptedException e) {
            unlock(rLockList);
            throw new BusinessException(BusinessCode.CODE_811001);
        }
        return rLockList;
    }
    /** 
     *
     * @Author cuiyueqiang
     * @Description skuId锁批量解锁
     * @Date 2019/1/21 10:46 
     * @param rLockList
     * @return 
     **/
    private void unlock(List<RLock> rLockList){
        if(CollectionUtils.isEmpty(rLockList)){
            return;
        }
        for(RLock lock : rLockList){
            lock.unlock();
            String skuIdKey = lock.getName();
            String skuId = skuIdKey.substring(skuIdKey.indexOf(":")+1);
            logger.info("skuId:" + skuId +"=======================================已解锁");
        }
    }
    
    /** 
     *
     * @Author cuiyueqiang
     * @Description 处理业务逻辑，增减库存及同步es
     * @Date 2019/1/17 18:31 
     * @param condition
     * @return 
     **/
    private int handleStock(List<ProductStockSkuCondition> condition){
        //根据sku集合查询sku表并组装spu集合
        List<ProductStockSpuCondition> spuList = getSpuList(condition);
        logger.info("组装spuList集合 ================= " + JsonUtil.toJSONString(spuList));
        //将spuList,skuList放入一个对象中
        ProductStock productStock = new ProductStock();
        productStock.setSkuList(condition);
        productStock.setSpuList(spuList);
        //一条sql更新各自库存及销量
        int updateSkuCount = productSkuDao.updateProductStock(productStock);
        logger.info("库存及销量更新记录数 ================= " + updateSkuCount);
        //同步ES组装入参
        List<SkuChanged> changeSku = getChangeSku(condition);
        logger.info("同步ES入参 ====================== " + JsonUtil.toJSONString(changeSku));
        //调用search服务
        Boolean booleanResult= productSearchClient.updateSku(changeSku).getData();
        if (false == booleanResult){
            logger.error(booleanResult + "===============================同步ES失败!");
            throw new BusinessException(BusinessCode.CODE_811002);
        }
        logger.info(booleanResult + "===============================同步ES成功!");
        return updateSkuCount;
    }


    /** 
     *
     * @Author cuiyueqiang
     * @Description 根据sku集合查询并组装spu集合
     * @Date 2019/1/16 14:04 
     * @param condition
     * @return 
     **/
    private  List<ProductStockSpuCondition> getSpuList(List<ProductStockSkuCondition> condition){
        //sku集合转换map，如果list存在相同sku，累加数量
        Map<Long, ProductStockSkuCondition> map = new HashMap<>(16);
        for (ProductStockSkuCondition skuStock : condition) {
            if (map.get(skuStock.getSkuId()) == null) {
                map.put(skuStock.getSkuId(), skuStock);
                continue;
            }
            ProductStockSkuCondition skuCondition = map.get(skuStock.getSkuId());
            skuCondition.setSkuNum(skuCondition.getSkuNum() + skuStock.getSkuNum());
        }
        //根据当前skuid查询sku表中的spuId,skuId
        List<ProductSpuIdVO> productSpuIdVO = getProductSpuIdVO(condition);
        //新建spu
        Map<Long, ProductStockSpuCondition> productSpuMap = new HashMap<>(16);
        for (ProductSpuIdVO productSpu : productSpuIdVO) {
            ProductStockSkuCondition productStockSkuCondition = map.get(productSpu.getProductSkuId());
            Long skuNum = productStockSkuCondition.getSkuNum();
            //买入数量大于库存
            if (skuNum > productSpu.getStock()){
                throw new BusinessException(BusinessCode.CODE_401011);
            }
            //sku数量
            ProductStockSpuCondition stockSpuCondition = productSpuMap.get(productSpu.getProductSpuId());
            //第一次为null时，往productSpuMap中放入spuId及对象
            if(null == stockSpuCondition){
                stockSpuCondition = new ProductStockSpuCondition();
                stockSpuCondition.setSpuId(productSpu.getProductSpuId());
                stockSpuCondition.setSpuNum(productStockSkuCondition.getSkuNum());
                productSpuMap.put(productSpu.getProductSpuId(), stockSpuCondition);
            } else {
                //属于同一个spuid，数量相加
                stockSpuCondition.setSpuNum(productStockSkuCondition.getSkuNum() + stockSpuCondition.getSpuNum());
            }
        }
        //spu集合
        List<ProductStockSpuCondition> spuList = new ArrayList<ProductStockSpuCondition>(productSpuMap.values());
        return spuList;
    }

    /**
     * @Author cuiyueqiang
     * @Description 同步ES组装入参
     * @Date 2019/1/15 15:00
     * @param productSkuList
     * @return
     **/
    private List<SkuChanged> getChangeSku(List<ProductStockSkuCondition> productSkuList){
        //查询当前更改的sku剩余库存及销量等
        List<ProductSpuIdVO> productVO = getProductSpuIdVO(productSkuList);
        //查询当前sku对应的价格方案id
        List<PriceProgramIdVO> priceProgramIdVO = productPriceSkuDao.selectPriceProgramIdByskuId(productSkuList);

        Map<Long, ProductSpuIdVO> productVOMap = new HashMap<>(16);
        for (ProductSpuIdVO product : productVO) {
            if (productVOMap.get(product.getProductSkuId()) == null) {
                productVOMap.put(product.getProductSkuId(), product);
                continue;
            }
        }
        Map<Long, PriceProgramIdVO> priceProgramIdMap = new HashMap<>(16);
        //调用es入参集合
        List<SkuChanged> changeSku = new ArrayList<SkuChanged>();

        List<Long> priceProgramIdList = null;
        SkuChanged skuChange = null;
        //遍历PriceProgramIdVO通过map将同一个skuid的PriceProgramId加入priceProgramIdList
        for (PriceProgramIdVO priceProgram : priceProgramIdVO) {
            ProductSpuIdVO productSku =  productVOMap.get(priceProgram.getSkuId());
            PriceProgramIdVO priceProgramVO = priceProgramIdMap.get(priceProgram.getSkuId());

            if (null == priceProgramVO){
                skuChange = new SkuChanged();
                priceProgramIdList = new ArrayList<>();
                skuChange.setColorAttributeOptionId(productSku.getColorAttributeOptionId());
                skuChange.setSalesVolume(productSku.getSalesVolume()+productSku.getInitSalesVolume());
                skuChange.setStock(productSku.getStock());
                skuChange.setSkuId(productSku.getProductSkuId());
                skuChange.setSpuId(productSku.getProductSpuId());
                priceProgramIdList.add(priceProgram.getPriceProgramId());
                skuChange.setPriceProgramIdList(priceProgramIdList);
                changeSku.add(skuChange);

                priceProgramVO = new PriceProgramIdVO();
                priceProgramVO.setPriceProgramId(priceProgram.getPriceProgramId());
                priceProgramVO.setSkuId(productSku.getProductSkuId());
                priceProgramIdMap.put(productSku.getProductSkuId(),priceProgramVO);
            }else{
                priceProgramIdList.add(priceProgram.getPriceProgramId());
            }
        }
        return changeSku;
    }

    /**
     * @Author cuiyueqiang
     * @Description 根据skuId查询对应spuId及其他字段信息
     * @Date 2019/1/16 14:23
     * @param productSkuList
     * @return
     **/
    private  List<ProductSpuIdVO> getProductSpuIdVO(List<ProductStockSkuCondition> productSkuList){
        List<ProductSpuIdVO> productVO = productSkuDao.selectSpuIdBySkuList(productSkuList);
        if (CollectionUtils.isEmpty(productVO)){
            throw new BusinessException(BusinessCode.CODE_1024);
        }
        logger.info("根据当前skuid查询sku表中的spuId,skuId返回 ================= " + JsonUtil.toJSONString(productVO));
        return productVO;
    }
    /** 
     *
     * @Author cuiyueqiang
     * @Description 根据skuId排序
     * @Date 2019/1/17 17:31 
     * @param condition
     * @return 
     **/
    private List<ProductStockSkuCondition> getSortBySkuId(List<ProductStockSkuCondition> condition){
        Collections.sort(condition, new Comparator<ProductStockSkuCondition>(){
            @Override
            public int compare(ProductStockSkuCondition o1, ProductStockSkuCondition o2) {
                if(o1.getSkuId() > o2.getSkuId()){
                    return 1;
                }
                if(o1.getSkuId() == o2.getSkuId()){
                    return 0;
                }
                return -1;
            }
        });
        return condition;
    }
}
